#include "../inc/app_manager_app.h"
#include "../inc/app_manager_ui.h"

#if APP_MANAGER_LIB_ENABLE

typedef struct app_struct
{
    app_register_t app;
#if APP_MANAGER_ENABLE_APP_UNINSTALL
    app_install_status_t install_status;
#endif
    struct app_struct *next;
} app_struct_t;

static app_struct_t *app_list_installed_head = NULL;
static app_struct_t *app_list_installed_tail = NULL;
static uint8_t app_installed_number = 0;
static app_register_t *forground_app = NULL;
static void *userdata_tmp = NULL;
#if APP_MANAGER_ENABLE_APP_UNINSTALL
static app_struct_t *app_list_uninstalled_head = NULL;
static app_struct_t *app_list_uninstalled_tail = NULL;
static uint8_t app_uninstalled_number = 0;
#endif
#if APP_MANAGER_SUPPORT_RTOS
extern app_manager_mutex_cb_t app_manager_mutex_cb;
#endif

extern app_manager_ret_t app_manager_ui_load(void *ui, void *anim, uint16_t time, uint16_t delay);
extern void *app_manager_malloc(size_t size);
void app_manager_free(void *ptr);

static app_register_t *found_app_by_name(const char *app_name, app_struct_t *app_list_head)
{
    while (1)
    {
        if (app_list_head->app.name != NULL && strcmp(app_list_head->app.name, app_name) == 0)
            return &app_list_head->app;
        else if (app_list_head->next == NULL)
            return NULL;
        else
            app_list_head = app_list_head->next;
    }
}

app_manager_ret_t app_manager_register_app(app_register_t *app)
{
    app_manager_ret_t ret = APP_MANAGER_SUCCESS;
#if APP_MANAGER_SUPPORT_RTOS
    app_manager_mutex_cb.mutex_get_cb();
#endif

    app_struct_t *new_app = app_manager_malloc(sizeof(app_struct_t));
    if (new_app == NULL)
    {
        ret = APP_MANAGER_MALLOC_FAIL;
        goto exit;
    }
    memcpy(&new_app->app, app, sizeof(app_register_t));
    new_app->next = NULL;

#if APP_MANAGER_ENABLE_APP_UNINSTALL
    new_app->install_status = app_chek_uninstalled(app->name);
    if (new_app->install_status == APP_INSTALLED)
    {
#endif
        app_installed_number++;
        if (app_list_installed_head == NULL)
        {
            app_list_installed_head = new_app;
            app_list_installed_tail = app_list_installed_head;
        }
        else
        {
            app_list_installed_tail->next = new_app;
            app_list_installed_tail = new_app;
        }
        if (app->init != NULL)
            app->init(NULL);
#if APP_MANAGER_ENABLE_APP_UNINSTALL
    }
    else
    {
        app_uninstalled_number++;
        if (app_list_uninstalled_head == NULL)
        {
            app_list_uninstalled_head = new_app;
            app_list_uninstalled_tail = app_list_uninstalled_head;
        }
        else
        {
            app_list_uninstalled_tail->next = new_app;
            app_list_uninstalled_tail = new_app;
        }
    }
#endif

exit:
#if APP_MANAGER_SUPPORT_RTOS
    app_manager_mutex_cb.mutex_give_cb();
#endif

    return ret;
}

app_manager_ret_t app_manager_unregister_app(const char *app_name)
{
    app_manager_ret_t ret = APP_MANAGER_SUCCESS;

    if (app_name == NULL || app_list_installed_head == NULL)
        return APP_MANAGER_NAME_NONE;

#if APP_MANAGER_SUPPORT_RTOS
    app_manager_mutex_cb.mutex_get_cb();
#endif

    app_struct_t *app_tmp = app_list_installed_head;
    app_struct_t *app_prev = NULL;
    while (app_tmp->next != NULL)
    {
        if (strcmp(app_tmp->app.name, app_name) == 0) // 找到了名字
        {
            if (app_tmp == app_list_installed_head) // 头
            {
                app_list_installed_head = app_list_installed_head->next;
                app_manager_free(app_tmp);
            }
            else if (app_tmp == app_list_installed_tail) // 尾
            {
                app_prev->next = NULL;
                app_list_installed_tail = app_prev;
                app_manager_free(app_tmp);
            }
            else // 中间
            {
                app_prev->next = app_tmp->next;
                app_manager_free(app_tmp);
            }
            goto exit;
        }
        app_prev = app_tmp;
        app_tmp = app_tmp->next;
    }

    ret = APP_MANAGER_NAME_NONE;

exit:
#if APP_MANAGER_SUPPORT_RTOS
    app_manager_mutex_cb.mutex_give_cb();
#endif

    return ret;
}

uint8_t app_manager_get_installed_number()
{
    return app_installed_number;
}

app_register_t *app_manager_get_installed_app(uint8_t app_id, const char *app_name)
{
    app_register_t *app = NULL;
#if APP_MANAGER_SUPPORT_RTOS
    app_manager_mutex_cb.mutex_get_cb();
#endif

    if (app_name != NULL)
    {
        app = found_app_by_name(app_name, app_list_installed_head);
        goto exit;
    }

    if (app_id >= app_installed_number)
        goto exit;
    app_struct_t *app_tmp = app_list_installed_head;
    for (size_t i = 0; i < app_id; i++)
    {
        app_tmp = app_tmp->next;
    }
    app = &app_tmp->app;

exit:
#if APP_MANAGER_SUPPORT_RTOS
    app_manager_mutex_cb.mutex_give_cb();
#endif

    return app;
}

#if APP_MANAGER_ENABLE_APP_UNINSTALL // todo
uint8_t app_manager_get_uninstalled_number()
{
    return app_uninstalled_number;
}

app_register_t *app_manager_get_uninstalled_app(uint8_t app_id, const char *app_name)
{
    app_register_t *app = NULL;
#if APP_MANAGER_SUPPORT_RTOS
    app_manager_mutex_cb.mutex_get_cb();
#endif

    if (app_name != NULL)
    {
        app = found_app_by_name(app_name, app_list_uninstalled_head);
        goto exit;
    }

    if (app_id >= app_uninstalled_number)
        goto exit;
    app_struct_t *app_tmp = app_list_uninstalled_head;
    for (size_t i = 0; i < app_id; i++)
    {
        app_tmp = app_tmp->next;
    }
    app = &app_tmp->app;

exit:
#if APP_MANAGER_SUPPORT_RTOS
    app_manager_mutex_cb.mutex_give_cb();
#endif

    return app;
}
#endif

app_manager_ret_t app_manager_config_struct_init(app_register_t *app)
{
    if (app == NULL)
        return APP_MANAGER_ERROR;
    memset(app, 0, sizeof(app_register_t));
    return APP_MANAGER_SUCCESS;
}

app_manager_ret_t app_manager_open(uint8_t app_id, const char *app_name, void *anim, uint16_t time, uint16_t delay)
{
    app_register_t *app = NULL;
    app_manager_ret_t ret = APP_MANAGER_SUCCESS;
#if APP_MANAGER_SUPPORT_RTOS
    app_manager_mutex_cb.mutex_get_cb();
#endif

    if (app_name != NULL)
    {
        app = found_app_by_name(app_name, app_list_installed_head);
    }
    else if (app_id < app_installed_number) // use id
    {
        app_struct_t *tmp = app_list_installed_head;
        for (size_t i = 0; i < app_id; i++)
            tmp = tmp->next;
        app = &tmp->app;
    }

    if (app == NULL)
    {
        ret = APP_MANAGER_NAME_NONE;
        goto exit;
    }

    if (app->ui_type == APP_UI_NONE)
    {
        if (app->load_to_background != NULL)
            app->load_to_background(userdata_tmp);
    }
    else
    {
        if (forground_app != NULL && forground_app->load_to_background != NULL)
            forground_app->load_to_background(NULL);
        void *ui = app->create_ui(NULL);
        if (app->load_to_foreground != NULL)
            app->load_to_foreground(userdata_tmp);
        app_manager_ui_load(ui, anim, time, delay);
        forground_app = app;
    }

exit:
#if APP_MANAGER_SUPPORT_RTOS
    app_manager_mutex_cb.mutex_give_cb();
#endif

    return ret;
}

app_manager_ret_t app_manager_open_with_userdata(uint8_t app_id, const char *app_name, void *anim, uint16_t time, uint16_t delay, void *userdata)
{
    userdata_tmp = userdata;
    app_manager_ret_t ret = app_manager_open(app_id, app_name, anim, time, delay);
    userdata_tmp = NULL;
    return ret;
}

app_register_t *app_manager_get_forground_app()
{
    return forground_app;
}

uint8_t app_manager_get_installed_app_id(const char *name)
{
    uint8_t id = 0;
#if APP_MANAGER_SUPPORT_RTOS
    app_manager_mutex_cb.mutex_get_cb();
#endif

    if (app_list_installed_head == NULL)
    {
        id = 255;
        goto exit;
    }

    app_struct_t *app_list_head = app_list_installed_head;
    while (1)
    {
        if (strcmp(app_list_head->app.name, name) == 0)
        {
            goto exit;
        }
        else if (app_list_head->next == NULL)
        {
            id = 255;
            goto exit;
        }
        app_list_head = app_list_head->next;
        id++;
    }

exit:
#if APP_MANAGER_SUPPORT_RTOS
    app_manager_mutex_cb.mutex_give_cb();
#endif

    return id;
}

#endif // #if APP_MANAGER_LIB_ENABLE
